/*
Simple Forum Auto Poster.
Copyright (C) 2011 Gianni Van Hoecke.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package persistence;

import domain.DomainController;
import domain.cipher.CipherManager;
import domain.forumdata.Categories;
import domain.forumdata.Forums;
import domain.forumdata.Queue;
import domain.history.History;
import domain.settings.Preferences;
import domain.settings.Settings;
import domain.template.Templates;
import domain.ui.LookAndFeel;
import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.InputStreamReader;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.PrintWriter;
import java.util.logging.Level;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import javax.crypto.Cipher;
import javax.crypto.SealedObject;

/**
 *
 * @author Gianni Van Hoecke <gianni.vh@gmail.com>
 *
 * Contains all I/O operations.
 */
public class PersistenceController
{
    /**
     * Verifies if the application folder exists. If not, it will be created.
     */
    public static void checkDir()
    {
        try
        {
            File file = new File(Settings.APP_DIR);
            if(!file.exists())
            {
                DomainController.getInstance().getLogger().log(Level.INFO, "Could not find app directory, creating...");
                file.mkdirs();
            }
        }
        catch(Exception e)
        {
            DomainController.getInstance().getLogger().log(Level.SEVERE, e.getMessage());
        }
    }

    /**
     * Saves the categories to disk.
     * @param categories The categories.
     */
    public static void saveCategories(Categories categories)
    {
        FileOutputStream f_out = null;
        ObjectOutputStream obj_out = null;

        try
        {
            f_out = new FileOutputStream(Settings.CATEGORIES_FILE);
            obj_out = new ObjectOutputStream(f_out);
            obj_out.writeObject(categories);
            DomainController.getInstance().getLogger().log(Level.INFO, "Categories saved!");
        }
        catch(Exception e)
        {
            DomainController.getInstance().getLogger().log(Level.SEVERE, e.getMessage());
        }
        finally
        {
            try
            {
                if (obj_out != null)
                    obj_out.close();

                if (f_out != null)
                    f_out.close();
            }
            catch (Exception ex)
            {
                DomainController.getInstance().getLogger().log(Level.SEVERE, ex.getMessage());
            }
            finally
            {
                obj_out = null;
                f_out = null;
            }
        }
    }

    /**
     * @return The previous saved categories.
     */
    public static Categories loadCategories()
    {
        FileInputStream f_in = null;
        ObjectInputStream obj_in = null;
        Categories categories = null;

        try
        {
            f_in = new FileInputStream(Settings.CATEGORIES_FILE);
            obj_in = new ObjectInputStream(f_in);
            categories = (Categories)obj_in.readObject();
            DomainController.getInstance().getLogger().log(Level.INFO, "Categories loaded!");
        }
        catch(Exception e)
        {
            //Probably no settings to load, create a new one.
            DomainController.getInstance().getLogger().log(Level.INFO, "No categories found, initiating new categories file...");
            categories = new Categories();
        }
        finally
        {
            try
            {
                if(obj_in != null)
                    obj_in.close();
                if(f_in != null)
                    f_in.close();
            }
            catch(Exception e)
            {
                DomainController.getInstance().getLogger().log(Level.SEVERE, e.getMessage());
            }
            finally
            {
                obj_in = null;
                f_in = null;
                return categories;
            }
        }
    }

    /**
     * Saves the forums to disk.
     * @param forums The forums to save.
     */
    public static void saveForums(Forums forums)
    {
        FileOutputStream f_out = null;
        ObjectOutputStream obj_out = null;

        try
        {
            f_out = new FileOutputStream(Settings.FORUMS_FILE);
            obj_out = new ObjectOutputStream(f_out);
            obj_out.writeObject(forums);
            DomainController.getInstance().getLogger().log(Level.INFO, "Forums saved!");
        }
        catch(Exception e)
        {
            DomainController.getInstance().getLogger().log(Level.SEVERE, e.getMessage());
        }
        finally
        {
            try
            {
                if (obj_out != null)
                    obj_out.close();

                if (f_out != null)
                    f_out.close();
            }
            catch (Exception ex)
            {
                DomainController.getInstance().getLogger().log(Level.SEVERE, ex.getMessage());
            }
            finally
            {
                obj_out = null;
                f_out = null;
            }
        }
    }

    /**
     * @return The previous saved forums.
     */
    public static Forums loadForums()
    {
        FileInputStream f_in = null;
        ObjectInputStream obj_in = null;
        Forums forums = null;

        try
        {
            f_in = new FileInputStream(Settings.FORUMS_FILE);
            obj_in = new ObjectInputStream(f_in);
            forums = (Forums)obj_in.readObject();
            DomainController.getInstance().getLogger().log(Level.INFO, "Forums loaded!");
        }
        catch(Exception e)
        {
            //Probably no settings to load, create a new one.
            DomainController.getInstance().getLogger().log(Level.INFO, "No forums found, initiating new forum file...");
            forums = new Forums();
        }
        finally
        {
            try
            {
                if(obj_in != null)
                    obj_in.close();
                if(f_in != null)
                    f_in.close();
            }
            catch(Exception e)
            {
                DomainController.getInstance().getLogger().log(Level.SEVERE, e.getMessage());
            }
            finally
            {
                obj_in = null;
                f_in = null;
                return forums;
            }
        }
    }

    /**
     * Saves the history entries to disk.
     * @param history The history.
     */
    public static void saveHistory(History history)
    {
        FileOutputStream f_out = null;
        ObjectOutputStream obj_out = null;

        try
        {
            CipherManager cm = new CipherManager();
            f_out = new FileOutputStream(Settings.HISTORY_FILE);
            obj_out = new ObjectOutputStream(new GZIPOutputStream(f_out));
            obj_out.writeObject(cm);
            obj_out.writeObject(new SealedObject(history, cm.getDesCipher()));
            DomainController.getInstance().getLogger().log(Level.INFO, "History saved!");
        }
        catch(Exception e)
        {
            e.printStackTrace();
            DomainController.getInstance().getLogger().log(Level.SEVERE, e.getMessage());
        }
        finally
        {
            try
            {
                if (obj_out != null)
                {
                    obj_out.flush();
                    obj_out.close();
                }

                if (f_out != null)
                    f_out.close();
            }
            catch (Exception ex)
            {
                DomainController.getInstance().getLogger().log(Level.SEVERE, ex.getMessage());
            }
            finally
            {
                obj_out = null;
                f_out = null;
            }
        }
    }

    /**
     * @return The previous saved history entries.
     */
    public static History loadHistory()
    {
        FileInputStream f_in = null;
        ObjectInputStream obj_in = null;
        History history = null;

        try
        {
            f_in = new FileInputStream(Settings.HISTORY_FILE);
            obj_in = new ObjectInputStream(new GZIPInputStream(f_in));
            CipherManager cm = (CipherManager)obj_in.readObject();
            SealedObject sealed = (SealedObject)obj_in.readObject();
            cm.setMode(Cipher.DECRYPT_MODE);
            history = (History)sealed.getObject(cm.getDesCipher());
            DomainController.getInstance().getLogger().log(Level.INFO, "History loaded!");
        }
        catch(Exception e)
        {
            e.printStackTrace();
            //Probably no settings to load, create a new one.
            DomainController.getInstance().getLogger().log(Level.INFO, "No history found, initiating new history file...");
            history = new History();
        }
        finally
        {
            try
            {
                if(obj_in != null)
                    obj_in.close();
                if(f_in != null)
                    f_in.close();
            }
            catch(Exception e)
            {
                DomainController.getInstance().getLogger().log(Level.SEVERE, e.getMessage());
            }
            finally
            {
                obj_in = null;
                f_in = null;
                return history;
            }
        }
    }

    /**
     * Saves the LAF to disk.
     * @param laf The LAF.
     */
    public static void saveLookAndFeel(LookAndFeel laf)
    {
        FileOutputStream f_out = null;
        ObjectOutputStream obj_out = null;

        try
        {
            f_out = new FileOutputStream(Settings.LAF_FILE);
            obj_out = new ObjectOutputStream(f_out);
            obj_out.writeObject(laf);
            DomainController.getInstance().getLogger().log(Level.INFO, "Look and feel saved!");
        }
        catch(Exception e)
        {
            DomainController.getInstance().getLogger().log(Level.SEVERE, e.getMessage());
        }
        finally
        {
            try
            {
                if (obj_out != null)
                    obj_out.close();

                if (f_out != null)
                    f_out.close();
            }
            catch (Exception ex)
            {
                DomainController.getInstance().getLogger().log(Level.SEVERE, ex.getMessage());
            }
            finally
            {
                obj_out = null;
                f_out = null;
            }
        }
    }

    /**
     * @return The previous saved LAF.
     */
    public static LookAndFeel loadLookAndFeel()
    {
        FileInputStream f_in = null;
        ObjectInputStream obj_in = null;
        LookAndFeel laf = null;

        try
        {
            f_in = new FileInputStream(Settings.LAF_FILE);
            obj_in = new ObjectInputStream(f_in);
            laf = (LookAndFeel)obj_in.readObject();
            DomainController.getInstance().getLogger().log(Level.INFO, "Look and feel loaded!");
        }
        catch(Exception e)
        {
            //Probably no settings to load, create a new one.
            DomainController.getInstance().getLogger().log(Level.INFO, "No look and feel found, using system look and feel...");
            laf = new LookAndFeel();
        }
        finally
        {
            try
            {
                if(obj_in != null)
                    obj_in.close();
                if(f_in != null)
                    f_in.close();
            }
            catch(Exception e)
            {
                DomainController.getInstance().getLogger().log(Level.SEVERE, e.getMessage());
            }
            finally
            {
                obj_in = null;
                f_in = null;
                return laf;
            }
        }
    }

    /**
     * Saves the templates to disk.
     * @param templates The templates to save.
     */
    public static void saveTemplates(Templates templates)
    {
        FileOutputStream f_out = null;
        ObjectOutputStream obj_out = null;

        try
        {
            f_out = new FileOutputStream(Settings.TEMPLATES_FILE);
            obj_out = new ObjectOutputStream(f_out);
            obj_out.writeObject(templates);
            DomainController.getInstance().getLogger().log(Level.INFO, "Templates saved!");
        }
        catch(Exception e)
        {
            DomainController.getInstance().getLogger().log(Level.SEVERE, e.getMessage());
        }
        finally
        {
            try
            {
                if (obj_out != null)
                    obj_out.close();

                if (f_out != null)
                    f_out.close();
            }
            catch (Exception ex)
            {
                DomainController.getInstance().getLogger().log(Level.SEVERE, ex.getMessage());
            }
            finally
            {
                obj_out = null;
                f_out = null;
            }
        }
    }

    /**
     * @return The previous saved templates.
     */
    public static Templates loadTemplates()
    {
        FileInputStream f_in = null;
        ObjectInputStream obj_in = null;
        Templates templates = null;

        try
        {
            f_in = new FileInputStream(Settings.TEMPLATES_FILE);
            obj_in = new ObjectInputStream(f_in);
            templates = (Templates)obj_in.readObject();
            DomainController.getInstance().getLogger().log(Level.INFO, "Templates loaded!");
        }
        catch(Exception e)
        {
            //Probably no settings to load, create a new one.
            DomainController.getInstance().getLogger().log(Level.INFO, "No templates found, initiating new templates file...");
            templates = new Templates();
        }
        finally
        {
            try
            {
                if(obj_in != null)
                    obj_in.close();
                if(f_in != null)
                    f_in.close();
            }
            catch(Exception e)
            {
                DomainController.getInstance().getLogger().log(Level.SEVERE, e.getMessage());
            }
            finally
            {
                obj_in = null;
                f_in = null;
                return templates;
            }
        }
    }

    /**
     * Saves text to disk.
     * @param saveTo The location to save to.
     * @param log The text to save.
     */
    public static void saveText(File saveTo, String log)
    {
        FileWriter outFile = null;
        PrintWriter out = null;

        try
        {
            outFile = new FileWriter(saveTo);
            out = new PrintWriter(outFile);
            out.println(log);
        }
        catch (Exception e)
        {
            DomainController.getInstance().getLogger().log(Level.SEVERE, e.getMessage());
        }
        finally
        {
            try
            {
                if(out != null)
                    out.close();
                if(outFile != null)
                    outFile.close();

                out = null;
                outFile = null;

                DomainController.getInstance().getLogger().log(Level.INFO, "Content saved to {0}", saveTo.getAbsolutePath());
            }
            catch(Exception ex)
            {
                DomainController.getInstance().getLogger().log(Level.SEVERE, ex.getMessage());
            }
        }
    }

    /**
     * @param file The location to load.
     * @return The content of the file.
     */
    public static String getText(File file)
    {
        String returnValue = "";

        FileInputStream fstream = null;
        DataInputStream in = null;
        BufferedReader br = null;

        try
        {
            fstream = new FileInputStream(file);
            in = new DataInputStream(fstream);
            br = new BufferedReader(new InputStreamReader(in));

            String strLine;

            while ((strLine = br.readLine()) != null)
            {
                returnValue += strLine + Settings.LINE_SEPARATOR;
            }
        }
        catch (Exception e)
        {
            System.err.println("Error: " + e.getMessage());
        }
        finally
        {
            try
            {
                if(br != null)
                    br.close();
                if(in != null)
                    in.close();
                if(fstream != null)
                    fstream.close();
            }
            catch(Exception e)
            {
                //Ignore
            }
            finally
            {
                return returnValue;
            }
        }
    }

    /**
     * Saves the queue to disk.
     * @param queue The queue to save.
     */
    public static void saveQueue(Queue queue)
    {
        FileOutputStream f_out = null;
        ObjectOutputStream obj_out = null;

        try
        {
            f_out = new FileOutputStream(Settings.QUEUE_FILE);
            obj_out = new ObjectOutputStream(new GZIPOutputStream(f_out));
            obj_out.writeObject(queue);
            DomainController.getInstance().getLogger().log(Level.INFO, "Queue saved!");
        }
        catch(Exception e)
        {
            DomainController.getInstance().getLogger().log(Level.SEVERE, e.getMessage());
        }
        finally
        {
            try
            {
                if (obj_out != null)
                    obj_out.close();

                if (f_out != null)
                    f_out.close();
            }
            catch (Exception ex)
            {
                DomainController.getInstance().getLogger().log(Level.SEVERE, ex.getMessage());
            }
            finally
            {
                obj_out = null;
                f_out = null;
            }
        }
    }
    
    /**
     * @return The queued posts list.
     */
    public static Queue loadQueue()
    {
        FileInputStream f_in = null;
        ObjectInputStream obj_in = null;
        Queue queue = null;

        try
        {
            f_in = new FileInputStream(Settings.QUEUE_FILE);
            obj_in = new ObjectInputStream(new GZIPInputStream(f_in));
            queue = (Queue)obj_in.readObject();
            DomainController.getInstance().getLogger().log(Level.INFO, "Queue loaded!");
        }
        catch(Exception e)
        {
            //Probably no settings to load, create a new one.
            DomainController.getInstance().getLogger().log(Level.INFO, "No queue found, initiating new queue file...");
            queue = new Queue();
        }
        finally
        {
            try
            {
                if(obj_in != null)
                    obj_in.close();
                if(f_in != null)
                    f_in.close();
            }
            catch(Exception e)
            {
                DomainController.getInstance().getLogger().log(Level.SEVERE, e.getMessage());
            }
            finally
            {
                obj_in = null;
                f_in = null;
                return queue;
            }
        }
    }

    /**
     * Saves the preferences to disk.
     * @param preferences The preferences.
     */
    public static void savePreferences(Preferences preferences)
    {
        FileOutputStream f_out = null;
        ObjectOutputStream obj_out = null;

        try
        {
            f_out = new FileOutputStream(Settings.PREFERENCES_FILE);
            obj_out = new ObjectOutputStream(f_out);
            obj_out.writeObject(preferences);
            DomainController.getInstance().getLogger().log(Level.INFO, "Preferences saved!");
        }
        catch(Exception e)
        {
            DomainController.getInstance().getLogger().log(Level.SEVERE, e.getMessage());
        }
        finally
        {
            try
            {
                if (obj_out != null)
                    obj_out.close();

                if (f_out != null)
                    f_out.close();
            }
            catch (Exception ex)
            {
                DomainController.getInstance().getLogger().log(Level.SEVERE, ex.getMessage());
            }
            finally
            {
                obj_out = null;
                f_out = null;
            }
        }
    }

    /**
     * @return The previous saved preferences.
     */
    public static Preferences loadPreferences()
    {
        FileInputStream f_in = null;
        ObjectInputStream obj_in = null;
        Preferences preferences = null;

        try
        {
            f_in = new FileInputStream(Settings.PREFERENCES_FILE);
            obj_in = new ObjectInputStream(f_in);
            preferences = (Preferences)obj_in.readObject();
            DomainController.getInstance().getLogger().log(Level.INFO, "Preferences loaded!");
        }
        catch(Exception e)
        {
            //Probably no settings to load, create a new one.
            DomainController.getInstance().getLogger().log(Level.INFO, "No preferences found, initiating new preferences file...");
            preferences = new Preferences();
        }
        finally
        {
            try
            {
                if(obj_in != null)
                    obj_in.close();
                if(f_in != null)
                    f_in.close();
            }
            catch(Exception e)
            {
                DomainController.getInstance().getLogger().log(Level.SEVERE, e.getMessage());
            }
            finally
            {
                obj_in = null;
                f_in = null;
                return preferences;
            }
        }
    }
}